package com.wh1200.mybatis.xmlless.model.dto

import com.wh1200.mybatis.xmlless.AnnotationSupport
import com.wh1200.mybatis.xmlless.annotations.*
import com.wh1200.mybatis.xmlless.annotations.criteria.Criteria
import com.wh1200.mybatis.xmlless.constant.Annotations
import com.wh1200.mybatis.xmlless.enums.TestType
import com.wh1200.mybatis.xmlless.model.ChooseCriteriaInfo
import com.wh1200.mybatis.xmlless.model.JoinObjectInfo
import com.wh1200.mybatis.xmlless.model.base.BaseAnnotatedElementWrapper
import com.wh1200.mybatis.xmlless.util.*
import org.apache.ibatis.type.TypeHandler
import java.lang.reflect.AnnotatedElement

/**
 * @author 吴昊
 * @date 2024/01/17
 * @version 1.0
 * @since v4.0.0
 */
abstract class DefaultAnnotatedElementWrapper<T : AnnotatedElement>(
    val element: T
) : BaseAnnotatedElementWrapper {

  override fun getUpdate(): String? {
    return AnnotationUtil.getAnnotationFieldValue<String>(
        element, Annotations.Plus.TABLE_FIELD, "update"
    )
  }

  override fun getElement(): Any {
    return element
  }

  override fun isNotNull(): Boolean {
    return getParameterTestTypeList().contains(TestType.NotNull)
  }

  override fun isNotBlank(): Boolean {
    return getParameterTestTypeList().contains(TestType.NotBlank)
  }

  override fun isVersion(): Boolean {
    return element.hasAnyAnnotation(*AnnotationSupport.VERSION_ANNOTATIONS)
  }

  override fun isNotEmpty(): Boolean {
    return getParameterTestTypeList().contains(TestType.NotEmpty)
  }

  override fun isInsertIgnore(): Boolean {
    return FieldUtil.isInsertIgnore(element)
  }

  override fun getPropertyMapTo(): String {
    return this.element.getAnnotation(PropertyMapping::class.java)?.value ?: ""
  }

  override fun isSelectIgnore(): Boolean {
    return FieldUtil.isSelectIgnore(element)
  }

  override fun isUpdateIgnore(): Boolean {
    return FieldUtil.isUpdateIgnore(element)
  }

  override fun isAutoGenerateKey(): Boolean {
    return AnnotationSupport.isAutoGeneratedKey(element)
  }

  override fun isCount(): Boolean {
    return element.isAnnotationPresent(Count::class.java)
  }

  override fun isJsonMappingProperty(): Boolean {
    return element.isAnnotationPresent(JsonMappingProperty::class.java)
  }

  override fun getSpecificName(): String? {
    return AnnotationUtil.getAnnotationFieldValue<String>(
        element, "org.apache.ibatis.annotations.Param", "value"
    )
  }

  override fun getCriteria(): Criteria? {
    return element.getAnnotation(Criteria::class.java)
  }

  override fun getJoinPropertyList(): List<JoinObjectInfo> {
    val list = element.getAnnotationsByType(FeignProperty::class.java).toList()
    return list.map {
      JoinObjectInfo(
          it.joinOnTargetProperty,
          it.joinOnThisProperty,
          it.alias,
          ClassWrapperProvider.createClassWrapper(it.targetEntity.java)
      )
    }
  }

  override fun getParameterTestTypeList(): List<TestType> {
    return element.getAnnotation(Test::class.java)?.value?.toList() ?: listOf()
  }

  override fun isNoIfWrap(): Boolean {
    return element.isAnnotationPresent(NoIfWrap::class.java)
  }

  override fun getTypeHandlerName(): String? {
    return resolveTypeHandlerName(element)
  }

  override fun getCustomTypeHandlerClass(): Class<out TypeHandler<*>>? {
    return element.getDeclaredAnnotation(Handler::class.java)?.value?.java
  }

  override fun getChooseCriteria(): ChooseCriteriaInfo {
    if (element.hasAnnotation(ChooseCriteria::class.java.name)) {
      return ChooseCriteriaInfo(element.getAnnotation(ChooseCriteria::class.java).value.map {
        parseTestCriteria(it)
      }, element.getAnnotation(ChooseCriteria::class.java).otherwise)
    }
    return ChooseCriteriaInfo(element.getAnnotationsByType(TestCriteria::class.java).toList(), "")
  }

  override fun isPrimitive(): Boolean {
    return getType().isPrimitive()
  }

  override fun isString(): Boolean {
    return getType().isString()
  }

  override fun isCollection(): Boolean {
    return getType().isCollection()
  }


  private fun resolveTypeHandlerName(element: AnnotatedElement): String? {
    val handlerAnno = element.getDeclaredAnnotation(Handler::class.java)
    if (handlerAnno != null) {
      return handlerAnno.value.java.name
    }
    if (element.isAnnotationPresent(JsonMappingProperty::class.java)) {
      return "com.wh1200.mybatis.xmlless.handler.JsonTypeHandler"
    }
    return null
  }

}
